/*
 * Andrew Boguk
 *
 * Scheduler ADT that handles context switches and semaphores.
 */

#include <stdio.h>
#include "queue.h"

/*
 * sched.h : here while header conflicts are resolved.
 */
#define MAX_ELEMENTS 100
#define INT_TIMER 0
#define INT_IO_SERVICE 1
#define INT_IO_REQUEST 2

#ifndef SCHED_H_
#define SCHED_H_

	typedef struct Sched_Struct* Scheduler;
	typedef struct PCB_Struct* PCB;

	Scheduler sched_create(Queue ready_queue);
	void sched_switch(Scheduler sched, int type);
	PCB sched_getRunning(Scheduler sched);
	void sched_printBlocked(Scheduler sched);

	struct Sched_Struct
	{
		Queue ready;
		Queue blocked;
		PCB running;
	};

	struct PCB_Struct
	{
		int id;
	};

#endif /* SCHED_H_ */

//End Header


/*
 * Creates and initializes scheduler.
 *
 * @param ready_queue : Queue full of PCB structs.
 * @return : Scheduler ADT used with the other functions.
 */
Scheduler sched_create(Queue ready_queue)
{
	Scheduler sched;
	sched = (Scheduler)malloc(sizeof(struct Sched_Struct));

	sched->ready = ready_queue;
	sched->blocked = queue_CreateQueue(MAX_ELEMENTS);
	sched->running = queue_FrontAndDequeue(ready_queue);

	return sched;
}

/*
 * Does the context switch depending on what type of interrupt
 * gets passed into the function.
 *
 * Currently the function is not modular for simplicity sake, will
 * make more flexible once things come together.
 *
 * @param sched : Scheduler ADT
 * @param type : The type of interrupt that is triggering the context switch.
 */
void sched_switch(Scheduler sched, int type)
{
	switch (type)
	{
	case INT_TIMER:
		queue_Enqueue(sched->running, sched->ready);
		sched->running = queue_FrontAndDequeue(sched->ready);
		break;
	case INT_IO_SERVICE:
		queue_Enqueue(queue_FrontAndDequeue(sched->blocked), sched->ready);
		break;
	case INT_IO_REQUEST:
		queue_Enqueue(sched->running, sched->blocked);
		sched->running = queue_FrontAndDequeue(sched->ready);
		break;
	}
}

/*
 * Returns the current PCB struct set as running.
 *
 * @param sched : Scheduler ADT
 * @return PCB : Currently running PCB
 */
PCB sched_getRunning(Scheduler sched)
{
	return sched->running;
}

/*
 * Testing purposes. Prints out size of blocked queue.
 */
void sched_printBlocked(Scheduler sched)
{
	int size = sched->blocked->Size;

	printf("\n[");
	printf("%d", size);
	printf("]\n");
}

int main()
{

	Queue q = queue_CreateQueue(MAX_ELEMENTS);

	PCB str1 = (PCB)malloc(sizeof(struct PCB_Struct));
	str1->id = 1;

	PCB str2 = (PCB)malloc(sizeof(struct PCB_Struct));
	str2->id = 2;

	PCB str3 = (PCB)malloc(sizeof(struct PCB_Struct));
	str3->id = 3;

	queue_Enqueue(str1, q);
	queue_Enqueue(str2, q);
	queue_Enqueue(str3, q);

	Scheduler s = sched_create(q);

	//Testing
	PCB running;

	running = sched_getRunning(s);
	printf("%d\n", running->id);
	sched_printBlocked(s);

	sched_switch(s, INT_TIMER);

	running = sched_getRunning(s);
	printf("%d\n", running->id);
	sched_printBlocked(s);

	sched_switch(s, INT_IO_REQUEST);

	running = sched_getRunning(s);
	printf("%d\n", running->id);
	sched_printBlocked(s);

	sched_switch(s, INT_IO_SERVICE);

	running = sched_getRunning(s);
	printf("%d\n", running->id);
	sched_printBlocked(s);

	return 0;
}
